package org.robotfish.learn;

import java.util.Formatter;

import org.ejml.simple.SimpleMatrix;

public class GaussianFeatures extends Features {
	double[] kernels;
	double   width;
	int      feature;
	/** defines the cycle for Gaussian features, default is 0 */
	double   cycle;
		
	/**
	 * uses <b>feature</b> for Gaussians with no cycle
	 * @param kernels  centers of Gaussians
	 * @param sigma    (band)width
	 * @param feature  feature that will be using Gaussian
	 */
	public GaussianFeatures(double[] kernels, double sigma, int feature) {
		this(kernels, sigma, feature, 0);
	}

	/**
	 * @param kernels  centers of Gaussians
	 * @param sigma   gaussian bandwidth
	 * @param feature the feature to use
	 * @param cycle   period of the function (0 means no cycle)
	 */
	public GaussianFeatures(double[] kernels, double sigma, int feature, double cycle) {
		this.kernels = kernels;
		this.width = sigma;
		this.feature = feature;
		this.cycle = cycle;
	}

	@Override
	public int dimensions() {
		return kernels.length;
	}

	@Override
	public double[] feat(SimpleMatrix x) {
		double[] out = new double[kernels.length];
		if (cycle<=0) {
			// standard gaussian:
			for (int i=0; i<out.length; i++) {
				out[i] = gauss(x.get(feature), kernels[i]);
			}
		} else {
			// cycle gaussian:
			for (int i=0; i<out.length; i++) {
				out[i] = gaussCycle(x.get(feature), kernels[i]);
			}
		}
		return out;
	}
	
	private double gauss(double x1, double x2) {
		return Math.exp( -Math.pow( (x1-x2)/width, 2) );
	}

	private double gaussCycle(double x1, double x2) {
		double diff = Math.abs(x1-x2) % cycle;
		diff = Math.min(diff, cycle-diff);
		return Math.exp( -Math.pow( diff/width, 2) );
	}

	public double[] getKernels() {
		return kernels;
	}

	public void setKernels(double[] gaussians) {
		this.kernels = gaussians;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}

	public int getnFeat() {
		return feature;
	}

	public void setnFeat(int nFeat) {
		this.feature = nFeat;
	}

	public double getCycle() {
		return cycle;
	}

	public void setCycle(double cycle) {
		this.cycle = cycle;
	}

}
